package com.alg.local.gateway.web;

import com.alg.local.gateway.config.GatewayConstant;
import com.alg.local.gateway.entity.ClientAesKey;
import com.alg.local.gateway.entity.EncrProps;
import com.alg.local.gateway.util.*;
import com.alg.local.gateway.entity.AuthTypeProps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ServerWebExchange;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author al
 * @date 2022/5/31 10:07
 * @description
 */
@RestController
@Slf4j
public class EncryptDataController {

    @Autowired
    AuthTypeProps authTypeProps;

    @Autowired
    EncrProps encrProps;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    GatewayUtil gatewayUtil;

    @PostMapping("/putClientAesKey")
    public WebApiResult<Void> putClientAesKey(@RequestBody ClientAesKey clientAesKey) {
        try {
            String encAesKey = clientAesKey.getEncAesKey();
            String encAesIv = clientAesKey.getEncAesIv();
            if (StringUtils.isEmpty(encAesKey) || StringUtils.isEmpty(encAesIv)) {
                log.error("key or iv is empty!");
                return WebApiResult.error("key or iv is not allowed empty!");
            }
            log.info("PubKey加密后的key:{}", encAesKey);
            RMap<String, String> rsaKeyMap = redissonClient.getMap(GatewayConstant.RSA_KEY_MAP, new StringCodec());
            String priKey = rsaKeyMap.get(GatewayConstant.RSA_PRI_KEY);
            PrivateKey privateKey = AsymmetricEncryptionUtil.getPrivateKey(priKey);
            String aesKey = AsymmetricEncryptionUtil.build("RSA").setPrivateKey(privateKey).decrypt(encAesKey);
            String aesIv = AsymmetricEncryptionUtil.build("RSA").setPrivateKey(privateKey).decrypt(encAesIv);
            rsaKeyMap.put("key:" + clientAesKey.getClientId(), aesKey);
            rsaKeyMap.put("iv:" + clientAesKey.getClientId(), aesIv);
            return WebApiResult.ok();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("服务端接受客户端密钥异常，{}", e.getMessage());
            return WebApiResult.error(e.getMessage());
        }

    }

    @GetMapping("/getEncAesKeyOrIvBase64")
    public String getEncAesKeyOrIvBase64() {
        try {
            String key = Base64Util.encoder(PasswordEncryptionUtil.getSaltDefault());
            log.info("key = {}", key);
            RMap<String, String> encryptKeyMap = redissonClient.getMap(GatewayConstant.RSA_KEY_MAP, new StringCodec());
            String pubKey = encryptKeyMap.get(GatewayConstant.RSA_PUB_KEY);
            PublicKey publicKey = AsymmetricEncryptionUtil.getPublicKey(pubKey);

            return AsymmetricEncryptionUtil.build("RSA").setPublicKey(publicKey).encrypt(key);
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }


    @GetMapping("/getSystemPublicKey")
    public String getSystemPublicKey() {
        RMap<String, String> encryptKeyMap = redissonClient.getMap(GatewayConstant.RSA_KEY_MAP, new StringCodec());
        return encryptKeyMap.get(GatewayConstant.RSA_PUB_KEY);
    }


    @GetMapping("/encodeStr")
    public String encodeStr(@RequestParam("msg") String msg) {
        return Base64Util.encoder(msg.getBytes(StandardCharsets.UTF_8));
    }

    @GetMapping("/decodeStr")
    public String decodeStr(@RequestParam("msg") String msg) {
        return new String(Base64Util.decoder(msg), StandardCharsets.UTF_8);
    }

    @PostMapping("/encryptMsg")
    @CheckToken
    public String encryptMsg(@RequestBody Map<String, String> msg, ServerWebExchange exchange) {
        try {
            Map<String, String> clientAesKey = gatewayUtil.getClientAesKey(exchange.getRequest());
            return SymmetricEncryptionUtilCbc.encrypt(clientAesKey.get("key"), clientAesKey.get("iv"), msg.get("msg"));
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return "";
    }

    @PostMapping("/decryptMsg")
    @CheckToken
    public String decryptMsg(@RequestBody Map<String, String> msg, ServerWebExchange exchange) {
        try {
            Map<String, String> clientAesKey = gatewayUtil.getClientAesKey(exchange.getRequest());
            return SymmetricEncryptionUtilCbc.decrypt(clientAesKey.get("key"), clientAesKey.get("iv"), msg.get("msg"));
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return "";
    }

    @GetMapping("/getDefaultKey")
    @CheckToken
    public String getDefaultKey(ServerWebExchange exchange) {
        return encrProps.getAesKey();
    }

    @GetMapping("/getRandomKey")
    @CheckToken
    public String getRandomKey(@RequestParam("keyName") String keyName, ServerWebExchange exchange) {
        try {
            String key = Base64Util.encoder(PasswordEncryptionUtil.getSaltDefault());
            RMap<String, String> simpleMapTest = redissonClient.getMap(GatewayConstant.RSA_KEY_MAP, new StringCodec());
            simpleMapTest.put(keyName, key);
            return key;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    @GetMapping("/getDefaultIv")
    @CheckToken
    public String getDefaultIv(ServerWebExchange exchange) {
        return encrProps.getAesIv();
    }

    @GetMapping("/getRandomIv")
    @CheckToken
    public String getRandomIv(ServerWebExchange exchange) {
        try {
            SecureRandom sr = SecureRandom.getInstanceStrong();
            byte[] iv = sr.generateSeed(16);
            return Base64Util.encoder(iv);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    @GetMapping("/getTempToken")
    public String getTempToken() {
        try {
            RBucket<String> token = redissonClient.getBucket(GatewayConstant.TEMP_TOKEN, new StringCodec());
            if (token.isExists()) {
                return token.get();
            }

            RMap<String, String> rsaKeyMap = redissonClient.getMap(GatewayConstant.RSA_KEY_MAP, new StringCodec());
            String caStr = rsaKeyMap.get(GatewayConstant.RSA_PUB_KEY);
            PublicKey publicKey = AsymmetricEncryptionUtil.getPublicKey(caStr);
            String encrypt = AsymmetricEncryptionUtil.build("RSA").setPublicKey(publicKey).encrypt(authTypeProps.getGlobalToken().getSysUser());
            token.set(encrypt, 600, TimeUnit.SECONDS);
            return encrypt;
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return "";
    }

}
